1 module d_snprintf.snprintf; 2 3 /* $Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp $ */ 4 5 /* 6 * Copyright (c) 1995 Patrick Powell. 7 * 8 * This code is based on code written by Patrick Powell <papowell@astart.com>. 9 * It may be used for any purpose as long as this notice remains intact on all 10 * source code distributions. 11 */ 12 13 /* 14 * Copyright (c) 2008 Holger Weiss. 15 * 16 * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>. 17 * My changes to the code may freely be used, modified and/or redistributed for 18 * any purpose. It would be nice if additions and fixes to this file (including 19 * trivial code cleanups) would be sent back in order to let me include them in 20 * the version available at <http://www.jhweiss.de/software/snprintf.html>. 21 * However, this is not a requirement for using or redistributing (possibly 22 * modified) versions of this file, nor is leaving this notice intact mandatory. 23 */ 24 25 /* 26 * Copyright (c) 2018 KytoDragon. 27 * 28 * This version of the code is a port to the D programming language. 29 * Use however you like, as long as this and the previous notices remain intact. 30 */ 31 32 /* 33 * History 34 * 35 * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1: 36 * 37 * Fixed the detection of infinite floating point values on IRIX (and 38 * possibly other systems) and applied another few minor cleanups. 39 * 40 * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0: 41 * 42 * Added a lot of new features, fixed many bugs, and incorporated various 43 * improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery 44 * <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller 45 * <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH 46 * projects. The additions include: support the "e", "E", "g", "G", and 47 * "F" conversion specifiers (and use conversion style "f" or "F" for the 48 * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j", 49 * "t", and "z" length modifiers; support the "#" flag and the (non-C99) 50 * "'" flag; use localeconv(3) (if available) to get both the current 51 * locale's decimal point character and the separator between groups of 52 * digits; fix the handling of various corner cases of field width and 53 * precision specifications; fix various floating point conversion bugs; 54 * handle infinite and NaN floating point values; don't attempt to write to 55 * the output buffer (which may be NULL) if a size of zero was specified; 56 * check for integer overflow of the field width, precision, and return 57 * values and during the floating point conversion; use the OUTCHAR() macro 58 * instead of a function for better performance; provide asprintf(3) and 59 * vasprintf(3) functions; add new test cases. The replacement functions 60 * have been renamed to use an "rpl_" prefix, the function calls in the 61 * main project (and in this file) must be redefined accordingly for each 62 * replacement function which is needed (by using Autoconf or other means). 63 * Various other minor improvements have been applied and the coding style 64 * was cleaned up for consistency. 65 * 66 * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13: 67 * 68 * C99 compliant snprintf(3) and vsnprintf(3) functions return the number 69 * of characters that would have been written to a sufficiently sized 70 * buffer (excluding the '\0'). The original code simply returned the 71 * length of the resulting output string, so that's been fixed. 72 * 73 * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8: 74 * 75 * The original code assumed that both snprintf(3) and vsnprintf(3) were 76 * missing. Some systems only have snprintf(3) but not vsnprintf(3), so 77 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. 78 * 79 * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i: 80 * 81 * The PGP code was using unsigned hexadecimal formats. Unfortunately, 82 * unsigned formats simply didn't work. 83 * 84 * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1: 85 * 86 * Ok, added some minimal floating point support, which means this probably 87 * requires libm on most operating systems. Don't yet support the exponent 88 * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just 89 * wasn't being exercised in ways which showed it, so that's been fixed. 90 * Also, formatted the code to Mutt conventions, and removed dead code left 91 * over from the original. Also, there is now a builtin-test, run with: 92 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf 93 * 94 * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43: 95 * 96 * This was ugly. It is still ugly. I opted out of floating point 97 * numbers, but the formatter understands just about everything from the 98 * normal C string format, at least as far as I can tell from the Solaris 99 * 2.5 printf(3S) man page. 100 */ 101 102 /* 103 * ToDo 104 * 105 * - Add wide character support. 106 * - Add support for "%a" and "%A" conversions. 107 * - Create test routines which predefine the expected results. Our test cases 108 * usually expose bugs in system implementations rather than in ours :-) 109 */ 110 111 import d_snprintf.vararg; 112 113 alias snprintf = rpl_snprintf; 114 alias vsnprintf = rpl_vsnprintf!dummy_file_func; 115 116 alias snprintf_file_func = void function(void* file, ubyte[] data) nothrow @nogc; 117 alias snprintf_alloc_func = void* function(size_t size) nothrow @nogc; 118 119 private: 120 nothrow: 121 @nogc: 122 123 /* Support for uintmax_t. We also need UINTMAX_MAX. */ 124 alias uintmax_t = ulong; 125 /* Support for intmax_t. */ 126 alias intmax_t = long; 127 /* Support for uintptr_t. */ 128 alias uintptr_t = size_t; 129 /* 130 * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99: 131 * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an 132 * unsigned type if necessary. This should work just fine in practice. 133 */ 134 alias uptrdiff_t = size_t; 135 /* 136 * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7). 137 * However, we'll simply use size_t and convert it to a signed type if 138 * necessary. This should work just fine in practice. 139 */ 140 alias ssize_t = ptrdiff_t; 141 142 enum UINTMAX_MAX = ulong.max; 143 enum INT_MAX = int.max; 144 145 /* 146 * Buffer size to hold the octal string representation of UINT128_MAX without 147 * nul-termination ("3777777777777777777777777777777777777777777"). 148 */ 149 enum MAX_CONVERT_LENGTH = 43; 150 151 /* Format read states. */ 152 enum PRINT_S { 153 DEFAULT = 0, 154 FLAGS = 1, 155 WIDTH = 2, 156 DOT = 3, 157 PRECISION = 4, 158 MOD = 5, 159 CONV = 6, 160 } 161 162 /* Format flags. */ 163 enum PRINT_F { 164 MINUS = (1 << 0), 165 PLUS = (1 << 1), 166 SPACE = (1 << 2), 167 NUM = (1 << 3), 168 ZERO = (1 << 4), 169 QUOTE = (1 << 5), 170 UP = (1 << 6), 171 UNSIGNED = (1 << 7), 172 TYPE_G = (1 << 8), 173 TYPE_E = (1 << 9), 174 } 175 176 /* Conversion flags. */ 177 enum PRINT_C { 178 CHAR = 1, 179 SHORT = 2, 180 LONG = 3, 181 LLONG = 4, 182 LDOUBLE = 5, 183 SIZE = 6, 184 PTRDIFF = 7, 185 INTMAX = 8, 186 } 187 188 // We can't pass null as a template parameter of type function, so we use this as a comparison 189 void dummy_file_func(void* file, ubyte[] data) {} 190 191 pragma(inline, true) T MAX(T)(T x, T y) { return ((x >= y) ? x : y); } 192 pragma(inline, true) char CHARTOINT(char ch) { return cast(char)(ch - '0'); } 193 pragma(inline, true) bool ISDIGIT(char ch) { return ('0' <= ch && ch <= '9'); } 194 pragma(inline, true) bool ISNAN(real x) { return (x != x); } 195 pragma(inline, true) bool ISINF(real x) { return (x != 0.0 && x + x == x); } 196 197 int rpl_vsnprintf(alias file_func)(char[] str, string format, va_list args, void* file = null) { 198 size_t len = 0; 199 int overflow = 0; 200 int base = 0; 201 int cflags = 0; 202 int flags = 0; 203 int width = 0; 204 int precision = -1; 205 int state = PRINT_S.DEFAULT; 206 size_t format_index = 0; 207 size_t file_len = 0; 208 209 pragma(inline, true) void OUTCHAR(char ch) { 210 if (len < str.length) 211 str[len] = ch; 212 len++; 213 static if (&file_func != &dummy_file_func) { 214 if (file != null && len == str.length) { 215 file_func(file, cast(ubyte[])str); 216 file_len += len; 217 len = 0; 218 } 219 } 220 } 221 222 /* 223 * C99 says: "If `n' is zero, nothing is written, and `s' may be a null 224 * pointer." (7.19.6.5, 2) We're forgiving and allow a null pointer 225 * even if a size larger than zero was specified. At least NetBSD's 226 * snprintf(3) does the same, as well as other versions of this file. 227 * (Though some of these versions will write to a non-null buffer even 228 * if a size of zero was specified, which violates the standard.) 229 */ 230 if (str is null && str.length != 0) 231 str = str[0..0]; 232 233 big_loop: 234 while (format_index < format.length) { 235 char ch = format[format_index++]; 236 final switch (state) { 237 case PRINT_S.DEFAULT: 238 if (ch == '%') 239 state = PRINT_S.FLAGS; 240 else 241 OUTCHAR(ch); 242 break; 243 case PRINT_S.FLAGS: 244 switch (ch) { 245 case '-': 246 flags |= PRINT_F.MINUS; 247 break; 248 case '+': 249 flags |= PRINT_F.PLUS; 250 break; 251 case ' ': 252 flags |= PRINT_F.SPACE; 253 break; 254 case '#': 255 flags |= PRINT_F.NUM; 256 break; 257 case '0': 258 flags |= PRINT_F.ZERO; 259 break; 260 case '\'': /* SUSv2 flag (not in C99). */ 261 flags |= PRINT_F.QUOTE; 262 break; 263 default: 264 state = PRINT_S.WIDTH; 265 format_index--; 266 break; 267 } 268 break; 269 case PRINT_S.WIDTH: 270 if (ISDIGIT(ch)) { 271 ch = CHARTOINT(ch); 272 if (width > (INT_MAX - ch) / 10) { 273 overflow = 1; 274 break big_loop; 275 } 276 width = 10 * width + ch; 277 } else if (ch == '*') { 278 /* 279 * C99 says: "A negative field width argument is 280 * taken as a `-' flag followed by a positive 281 * field width." (7.19.6.1, 5) 282 */ 283 width = cast(int)get_any_int(args); 284 if (width < 0) { 285 flags |= PRINT_F.MINUS; 286 width = -width; 287 } 288 state = PRINT_S.DOT; 289 } else { 290 format_index--; 291 state = PRINT_S.DOT; 292 } 293 break; 294 case PRINT_S.DOT: 295 if (ch == '.') { 296 state = PRINT_S.PRECISION; 297 } else { 298 format_index--; 299 state = PRINT_S.MOD; 300 } 301 break; 302 case PRINT_S.PRECISION: 303 if (precision == -1) 304 precision = 0; 305 306 if (ISDIGIT(ch)) { 307 ch = CHARTOINT(ch); 308 if (precision > (INT_MAX - ch) / 10) { 309 overflow = 1; 310 break big_loop; 311 } 312 precision = 10 * precision + ch; 313 } else if (ch == '*') { 314 /* 315 * C99 says: "A negative precision argument is 316 * taken as if the precision were omitted." 317 * (7.19.6.1, 5) 318 */ 319 precision = cast(int)get_any_int(args); 320 if (precision < 0) 321 precision = -1; 322 state = PRINT_S.MOD; 323 } else { 324 format_index--; 325 state = PRINT_S.MOD; 326 } 327 break; 328 case PRINT_S.MOD: 329 switch (ch) { 330 case 'h': 331 if (format_index < format.length && format[format_index] == 'h') { 332 /* It's a char. */ 333 format_index++; 334 cflags = PRINT_C.CHAR; 335 } else 336 cflags = PRINT_C.SHORT; 337 break; 338 case 'l': 339 if (format_index < format.length && format[format_index] == 'l') { 340 /* It's a long long. */ 341 format_index++; 342 cflags = PRINT_C.LLONG; 343 } else 344 cflags = PRINT_C.LONG; 345 break; 346 case 'L': 347 cflags = PRINT_C.LDOUBLE; 348 break; 349 case 'j': 350 cflags = PRINT_C.INTMAX; 351 break; 352 case 't': 353 cflags = PRINT_C.PTRDIFF; 354 break; 355 case 'z': 356 cflags = PRINT_C.SIZE; 357 break; 358 default: 359 format_index--; 360 break; 361 } 362 state = PRINT_S.CONV; 363 break; 364 case PRINT_S.CONV: 365 switch (ch) { 366 case 'd': 367 /* FALLTHROUGH */ 368 case 'i': 369 intmax_t value; 370 switch (cflags) { 371 case PRINT_C.CHAR: 372 value = cast(char)get_any_int(args); 373 break; 374 case PRINT_C.SHORT: 375 value = cast(short)get_any_int(args); 376 break; 377 case PRINT_C.LONG: 378 value = cast(int)get_any_int(args); 379 break; 380 case PRINT_C.LLONG: 381 case PRINT_C.INTMAX: 382 value = cast(long)get_any_int(args); 383 break; 384 case PRINT_C.SIZE: 385 case PRINT_C.PTRDIFF: 386 value = cast(ptrdiff_t)get_any_int(args); 387 break; 388 default: 389 value = get_any_int(args); 390 break; 391 } 392 fmtint!file_func(str, &len, value, 10, width, 393 precision, flags, file, &file_len); 394 break; 395 case 'X': 396 flags |= PRINT_F.UP; 397 /* FALLTHROUGH */ 398 goto case 'x'; 399 case 'x': 400 base = 16; 401 /* FALLTHROUGH */ 402 goto case 'o'; 403 case 'o': 404 if (base == 0) 405 base = 8; 406 /* FALLTHROUGH */ 407 goto case 'u'; 408 case 'u': 409 if (base == 0) 410 base = 10; 411 uintmax_t value; 412 flags |= PRINT_F.UNSIGNED; 413 switch (cflags) { 414 case PRINT_C.CHAR: 415 value = cast(ubyte)get_any_int(args); 416 break; 417 case PRINT_C.SHORT: 418 value = cast(ushort)get_any_int(args); 419 break; 420 case PRINT_C.LONG: 421 value = cast(uint)get_any_int(args); 422 break; 423 case PRINT_C.LLONG: 424 case PRINT_C.INTMAX: 425 value = cast(ulong)get_any_int(args); 426 break; 427 case PRINT_C.SIZE: 428 case PRINT_C.PTRDIFF: 429 value = cast(size_t)get_any_int(args); 430 break; 431 default: 432 value = cast(uint)get_any_int(args); 433 break; 434 } 435 fmtint!file_func(str, &len, value, base, width, 436 precision, flags, file, &file_len); 437 break; 438 case 'A': 439 /* Not yet supported, we'll use "%F". */ 440 /* FALLTHROUGH */ 441 goto case 'F'; 442 case 'F': 443 flags |= PRINT_F.UP; 444 /* FALLTHROUGH */ 445 goto case 'a'; 446 case 'a': 447 /* Not yet supported, we'll use "%f". */ 448 /* FALLTHROUGH */ 449 goto case 'f'; 450 case 'f': 451 real fvalue; 452 if (cflags == PRINT_C.LDOUBLE) 453 fvalue = cast(real)get_any_float(args); 454 // Unlike C, D does not upconvert floats to double. You need to use %lf to print doubles. 455 else if (cflags == PRINT_C.LONG) 456 fvalue = cast(double)get_any_float(args); 457 else 458 fvalue = cast(float)get_any_float(args); 459 fmtflt!file_func(str, &len, fvalue, width, 460 precision, flags, &overflow, file, &file_len); 461 if (overflow) 462 break big_loop; 463 break; 464 case 'E': 465 flags |= PRINT_F.UP; 466 /* FALLTHROUGH */ 467 goto case 'e'; 468 case 'e': 469 real fvalue; 470 flags |= PRINT_F.TYPE_E; 471 if (cflags == PRINT_C.LDOUBLE) 472 fvalue = cast(real)get_any_float(args); 473 else if (cflags == PRINT_C.LONG) 474 fvalue = cast(double)get_any_float(args); 475 else 476 fvalue = cast(float)get_any_float(args); 477 fmtflt!file_func(str, &len, fvalue, width, 478 precision, flags, &overflow, file, &file_len); 479 if (overflow) 480 break big_loop; 481 break; 482 case 'G': 483 flags |= PRINT_F.UP; 484 /* FALLTHROUGH */ 485 goto case 'g'; 486 case 'g': 487 real fvalue; 488 flags |= PRINT_F.TYPE_G; 489 if (cflags == PRINT_C.LDOUBLE) 490 fvalue = cast(real)get_any_float(args); 491 else if (cflags == PRINT_C.LONG) 492 fvalue = cast(double)get_any_float(args); 493 else 494 fvalue = cast(float)get_any_float(args); 495 /* 496 * If the precision is zero, it is treated as 497 * one (cf. C99: 7.19.6.1, 8). 498 */ 499 if (precision == 0) 500 precision = 1; 501 fmtflt!file_func(str, &len, fvalue, width, 502 precision, flags, &overflow, file, &file_len); 503 if (overflow) 504 break big_loop; 505 break; 506 case 'c': 507 char cvalue; 508 if (va_get_type(args) is va_get_type!(char)) { 509 cvalue = va_arg!(char)(args); 510 } else { 511 cvalue = cast(char)get_any_int(args); 512 } 513 OUTCHAR(cvalue); 514 break; 515 case 's': 516 if (has_string_like_value(args)) { 517 string strvalue = va_arg!(string)(args); 518 if (precision == -1 || precision > strvalue.length) { 519 precision = cast(int)strvalue.length; 520 } 521 fmtstr!file_func(str, &len, strvalue.ptr, width, 522 precision, flags, file, &file_len); 523 } else { 524 const(char)* strvalue = cast(const(char)*)get_any_pointer(args); 525 fmtstr!file_func(str, &len, strvalue, width, 526 precision, flags, file, &file_len); 527 } 528 break; 529 case 'p': 530 /* 531 * C99 says: "The value of the pointer is 532 * converted to a sequence of printing 533 * characters, in an implementation-defined 534 * manner." (C99: 7.19.6.1, 8) 535 */ 536 void *strvalue = get_any_pointer(args); 537 if (strvalue == null) 538 /* 539 * We use the glibc format. BSD prints 540 * "0x0", SysV "0". 541 */ 542 fmtstr!file_func(str, &len, "(nil)", width, -1, flags, file, &file_len); 543 else { 544 /* 545 * We use the BSD/glibc format. SysV 546 * omits the "0x" prefix (which we emit 547 * using the PRINT_F.NUM flag). 548 */ 549 flags |= PRINT_F.NUM; 550 flags |= PRINT_F.UNSIGNED; 551 fmtint!file_func(str, &len, 552 cast(uintptr_t)strvalue, 16, width, 553 precision, flags, file, &file_len); 554 } 555 break; 556 case 'n': 557 switch (cflags) { 558 case PRINT_C.CHAR: 559 char* charptr = cast(char*)get_any_pointer(args); 560 *charptr = cast(char)(len + file_len); 561 break; 562 case PRINT_C.SHORT: 563 short* shortptr = cast(short *)get_any_pointer(args); 564 *shortptr = cast(short)(len + file_len); 565 break; 566 case PRINT_C.LONG: 567 int* longptr = cast(int *)get_any_pointer(args); 568 *longptr = cast(int)(len + file_len); 569 break; 570 case PRINT_C.LLONG: 571 long* llongptr = cast(long *)get_any_pointer(args); 572 *llongptr = cast(long)(len + file_len); 573 break; 574 case PRINT_C.SIZE: 575 /* 576 * C99 says that with the "z" length 577 * modifier, "a following `n' conversion 578 * specifier applies to a pointer to a 579 * signed integer type corresponding to 580 * size_t argument." (7.19.6.1, 7) 581 */ 582 ssize_t* sizeptr = cast(ssize_t *)get_any_pointer(args); 583 *sizeptr = cast(ssize_t)(len + file_len); 584 break; 585 case PRINT_C.INTMAX: 586 intmax_t* intmaxptr = cast(intmax_t *)get_any_pointer(args); 587 *intmaxptr = cast(intmax_t)(len + file_len); 588 break; 589 case PRINT_C.PTRDIFF: 590 ptrdiff_t* ptrdiffptr = cast(ptrdiff_t *)get_any_pointer(args); 591 *ptrdiffptr = cast(ptrdiff_t)(len + file_len); 592 break; 593 default: 594 int* intptr = cast(int *)get_any_pointer(args); 595 *intptr = cast(int)(len + file_len); 596 break; 597 } 598 break; 599 case '%': /* Print a "%" character verbatim. */ 600 OUTCHAR(ch); 601 break; 602 default: /* Skip other characters. */ 603 break; 604 } 605 state = PRINT_S.DEFAULT; 606 base = cflags = flags = width = 0; 607 precision = -1; 608 break; 609 } 610 } 611 612 613 static if (&file_func != &dummy_file_func) { 614 if (file != null && len > 0) { 615 file_func(file, cast(ubyte[])str[0..len]); 616 file_len += len; 617 len = file_len; 618 } 619 } else { 620 if (len < str.length) 621 str[len] = '\0'; 622 else if (str.length > 0) 623 str[$ - 1] = '\0'; 624 } 625 626 if (overflow || len >= INT_MAX) { 627 //errno = overflow ? EOVERFLOW : ERANGE; 628 return -1; 629 } 630 return cast(int)len; 631 } 632 633 void fmtstr(alias file_func)(char[] str, size_t *len, const(char)* value, int width, int precision, int flags, void* file, size_t* file_len) { 634 bool noprecision = (precision == -1); 635 636 pragma(inline, true) void OUTCHAR(char ch) { 637 if (*len + 1 <= str.length) 638 str[*len] = ch; 639 (*len)++; 640 static if (&file_func != &dummy_file_func) { 641 if (file != null && *len == str.length) { 642 file_func(file, cast(ubyte[])str); 643 *file_len += *len; 644 *len = 0; 645 } 646 } 647 } 648 649 if (value == null) /* We're forgiving. */ 650 value = "(null)"; 651 652 /* If a precision was specified, don't read the string past it. */ 653 int strln; 654 for (strln = 0; (noprecision || strln < precision) && 655 value[strln] != '\0'; strln++) 656 continue; 657 658 int padlen = width - strln; /* Amount to pad. */ 659 if (padlen < 0) 660 padlen = 0; 661 if (flags & PRINT_F.MINUS) /* Left justify. */ 662 padlen = -padlen; 663 664 while (padlen > 0) { /* Leading spaces. */ 665 OUTCHAR(' '); 666 padlen--; 667 } 668 while ((noprecision || precision-- > 0) && *value != '\0') { 669 OUTCHAR(*value); 670 value++; 671 } 672 while (padlen < 0) { /* Trailing spaces. */ 673 OUTCHAR(' '); 674 padlen++; 675 } 676 } 677 678 void fmtint(alias file_func)(char[] str, size_t *len, intmax_t value, int base, int width, 679 int precision, int flags, void* file, size_t* file_len) { 680 bool noprecision = (precision == -1); 681 682 pragma(inline, true) void OUTCHAR(char ch) { 683 if (*len + 1 <= str.length) 684 str[*len] = ch; 685 (*len)++; 686 static if (&file_func != &dummy_file_func) { 687 if (file != null && *len == str.length) { 688 file_func(file, cast(ubyte[])str); 689 *file_len += *len; 690 *len = 0; 691 } 692 } 693 } 694 695 uintmax_t uvalue; 696 char sign = 0; 697 if (flags & PRINT_F.UNSIGNED) 698 uvalue = value; 699 else { 700 uvalue = (value >= 0) ? value : -value; 701 if (value < 0) 702 sign = '-'; 703 else if (flags & PRINT_F.PLUS) /* Do a sign. */ 704 sign = '+'; 705 else if (flags & PRINT_F.SPACE) 706 sign = ' '; 707 } 708 709 char[MAX_CONVERT_LENGTH] iconvert; 710 int pos = convert(uvalue, iconvert, base, 711 flags & PRINT_F.UP); 712 713 char hexprefix = 0; 714 if (flags & PRINT_F.NUM && uvalue != 0) { 715 /* 716 * C99 says: "The result is converted to an `alternative form'. 717 * For `o' conversion, it increases the precision, if and only 718 * if necessary, to force the first digit of the result to be a 719 * zero (if the value and precision are both 0, a single 0 is 720 * printed). For `x' (or `X') conversion, a nonzero result has 721 * `0x' (or `0X') prefixed to it." (7.19.6.1, 6) 722 */ 723 switch (base) { 724 case 8: 725 if (precision <= pos) 726 precision = pos + 1; 727 break; 728 case 16: 729 hexprefix = (flags & PRINT_F.UP) ? 'X' : 'x'; 730 break; 731 default: 732 break; 733 } 734 } 735 736 int separators = 0; 737 if (flags & PRINT_F.QUOTE) /* Get the number of group separators we'll print. */ 738 separators = getnumsep(pos); 739 740 int zpadlen = precision - pos - separators; /* Amount to zero pad. */ 741 int spadlen = width /* Minimum field width. */ 742 - separators /* Number of separators. */ 743 - MAX(precision, pos) /* Number of integer digits. */ 744 - ((sign != 0) ? 1 : 0) /* Will we print a sign? */ 745 - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */ 746 747 if (zpadlen < 0) 748 zpadlen = 0; 749 if (spadlen < 0) 750 spadlen = 0; 751 752 /* 753 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is 754 * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a 755 * precision is specified, the `0' flag is ignored." (7.19.6.1, 6) 756 */ 757 if (flags & PRINT_F.MINUS) /* Left justify. */ 758 spadlen = -spadlen; 759 else if (flags & PRINT_F.ZERO && noprecision) { 760 zpadlen += spadlen; 761 spadlen = 0; 762 } 763 while (spadlen > 0) { /* Leading spaces. */ 764 OUTCHAR(' '); 765 spadlen--; 766 } 767 if (sign != 0) /* Sign. */ 768 OUTCHAR(sign); 769 if (hexprefix != 0) { /* A "0x" or "0X" prefix. */ 770 OUTCHAR('0'); 771 OUTCHAR(hexprefix); 772 } 773 while (zpadlen > 0) { /* Leading zeros. */ 774 OUTCHAR('0'); 775 zpadlen--; 776 } 777 while (pos > 0) { /* The actual digits. */ 778 pos--; 779 OUTCHAR(iconvert[pos]); 780 if (separators > 0 && pos > 0 && pos % 3 == 0) 781 OUTCHAR(','); 782 } 783 while (spadlen < 0) { /* Trailing spaces. */ 784 OUTCHAR(' '); 785 spadlen++; 786 } 787 } 788 789 void fmtflt(alias file_func)(char[] str, size_t *len, real fvalue, int width, 790 int precision, int flags, int *overflow, void* file, size_t* file_len) { 791 792 pragma(inline, true) void OUTCHAR(char ch) { 793 if (*len + 1 <= str.length) 794 str[*len] = ch; 795 (*len)++; 796 static if (&file_func != &dummy_file_func) { 797 if (file != null && *len == str.length) { 798 file_func(file, cast(ubyte[])str); 799 *file_len += *len; 800 *len = 0; 801 } 802 } 803 } 804 805 /* 806 * AIX' man page says the default is 0, but C99 and at least Solaris' 807 * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX 808 * defaults to 6. 809 */ 810 if (precision == -1) 811 precision = 6; 812 813 char sign = 0; 814 if (fvalue < 0.0) 815 sign = '-'; 816 else if (flags & PRINT_F.PLUS) /* Do a sign. */ 817 sign = '+'; 818 else if (flags & PRINT_F.SPACE) 819 sign = ' '; 820 821 const(char)* infnan = null; 822 if (ISNAN(fvalue)) 823 infnan = (flags & PRINT_F.UP) ? "NAN" : "nan"; 824 else if (ISINF(fvalue)) 825 infnan = (flags & PRINT_F.UP) ? "INF" : "inf"; 826 827 int ipos = 0; 828 if (infnan != null) { 829 char[MAX_CONVERT_LENGTH] iconvert; 830 if (sign != 0) 831 iconvert[ipos++] = sign; 832 while (*infnan != '\0') 833 iconvert[ipos++] = *infnan++; 834 fmtstr!file_func(str, len, iconvert.ptr, width, ipos, flags, file, file_len); 835 return; 836 } 837 838 839 /* "%e" (or "%E") or "%g" (or "%G") conversion. */ 840 int exponent = 0; 841 bool omitzeros = false; 842 bool estyle = (flags & PRINT_F.TYPE_E) != 0; 843 if (flags & PRINT_F.TYPE_E || flags & PRINT_F.TYPE_G) { 844 if (flags & PRINT_F.TYPE_G) { 845 /* 846 * For "%g" (and "%G") conversions, the precision 847 * specifies the number of significant digits, which 848 * includes the digits in the integer part. The 849 * conversion will or will not be using "e-style" (like 850 * "%e" or "%E" conversions) depending on the precision 851 * and on the exponent. However, the exponent can be 852 * affected by rounding the converted value, so we'll 853 * leave this decision for later. Until then, we'll 854 * assume that we're going to do an "e-style" conversion 855 * (in order to get the exponent calculated). For 856 * "e-style", the precision must be decremented by one. 857 */ 858 precision--; 859 /* 860 * For "%g" (and "%G") conversions, trailing zeros are 861 * removed from the fractional portion of the result 862 * unless the "#" flag was specified. 863 */ 864 if (!(flags & PRINT_F.NUM)) 865 omitzeros = true; 866 } 867 exponent = getexponent(fvalue); 868 estyle = true; 869 } 870 871 again: 872 /* 873 * Sorry, we only support 9, 19, or 38 digits (that is, the number of 874 * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value 875 * minus one) past the decimal point due to our conversion method. 876 */ 877 switch (uintmax_t.sizeof) { 878 case 16: 879 if (precision > 38) 880 precision = 38; 881 break; 882 case 8: 883 if (precision > 19) 884 precision = 19; 885 break; 886 default: 887 if (precision > 9) 888 precision = 9; 889 break; 890 } 891 892 real ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue; 893 if (estyle) /* We want exactly one integer digit. */ 894 ufvalue /= mypow10(exponent); 895 896 uintmax_t intpart = _cast(ufvalue); 897 if (intpart == UINTMAX_MAX) { 898 *overflow = 1; 899 return; 900 } 901 902 /* 903 * Factor of ten with the number of digits needed for the fractional 904 * part. For example, if the precision is 3, the mask will be 1000. 905 */ 906 uintmax_t mask = cast(uintmax_t)mypow10(precision); 907 /* 908 * We "cheat" by converting the fractional part to integer by 909 * multiplying by a factor of ten. 910 */ 911 uintmax_t fracpart = myround(mask * (ufvalue - intpart)); 912 if (fracpart >= mask) { 913 /* 914 * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000 915 * (because precision = 3). Now, myround(1000 * 0.99962) will 916 * return 1000. So, the integer part must be incremented by one 917 * and the fractional part must be set to zero. 918 */ 919 intpart++; 920 fracpart = 0; 921 if (estyle && intpart == 10) { 922 /* 923 * The value was rounded up to ten, but we only want one 924 * integer digit if using "e-style". So, the integer 925 * part must be set to one and the exponent must be 926 * incremented by one. 927 */ 928 intpart = 1; 929 exponent++; 930 } 931 } 932 933 /* 934 * Now that we know the real exponent, we can check whether or not to 935 * use "e-style" for "%g" (and "%G") conversions. If we don't need 936 * "e-style", the precision must be adjusted and the integer and 937 * fractional parts must be recalculated from the original value. 938 * 939 * C99 says: "Let P equal the precision if nonzero, 6 if the precision 940 * is omitted, or 1 if the precision is zero. Then, if a conversion 941 * with style `E' would have an exponent of X: 942 * 943 * - if P > X >= -4, the conversion is with style `f' (or `F') and 944 * precision P - (X + 1). 945 * 946 * - otherwise, the conversion is with style `e' (or `E') and precision 947 * P - 1." (7.19.6.1, 8) 948 * 949 * Note that we had decremented the precision by one. 950 */ 951 if (flags & PRINT_F.TYPE_G && estyle && 952 precision + 1 > exponent && exponent >= -4) { 953 precision -= exponent; 954 estyle = false; 955 goto again; 956 } 957 958 char[4] econvert; /* "e-12" (without nul-termination). */ 959 int epos = 0; 960 if (estyle) { 961 char esign = 0; 962 if (exponent < 0) { 963 exponent = -exponent; 964 esign = '-'; 965 } else 966 esign = '+'; 967 968 /* 969 * Convert the exponent. The econvert.sizeof is 4. So, the 970 * econvert buffer can hold e.g. "e+99" and "e-99". We don't 971 * support an exponent which contains more than two digits. 972 * Therefore, the following stores are safe. 973 */ 974 epos = convert(exponent, econvert[0..2], 10, 0); 975 /* 976 * C99 says: "The exponent always contains at least two digits, 977 * and only as many more digits as necessary to represent the 978 * exponent." (7.19.6.1, 8) 979 */ 980 if (epos == 1) 981 econvert[epos++] = '0'; 982 econvert[epos++] = esign; 983 econvert[epos++] = (flags & PRINT_F.UP) ? 'E' : 'e'; 984 } 985 986 char[MAX_CONVERT_LENGTH] iconvert; 987 char[MAX_CONVERT_LENGTH] fconvert; 988 /* Convert the integer part and the fractional part. */ 989 ipos = convert(intpart, iconvert, 10, 0); 990 int fpos = 0; 991 if (fracpart != 0) /* convert() would return 1 if fracpart == 0. */ 992 fpos = convert(fracpart, fconvert, 10, 0); 993 994 int leadfraczeros = precision - fpos; 995 996 int omitcount = 0; 997 if (omitzeros) { 998 if (fpos > 0) /* Omit trailing fractional part zeros. */ 999 while (omitcount < fpos && fconvert[omitcount] == '0') 1000 omitcount++; 1001 else { /* The fractional part is zero, omit it completely. */ 1002 omitcount = precision; 1003 leadfraczeros = 0; 1004 } 1005 precision -= omitcount; 1006 } 1007 1008 /* 1009 * Print a decimal point if either the fractional part is non-zero 1010 * and/or the "#" flag was specified. 1011 */ 1012 bool emitpoint = false; 1013 if (precision > 0 || flags & PRINT_F.NUM) 1014 emitpoint = true; 1015 int separators = 0; 1016 if (flags & PRINT_F.QUOTE) /* Get the number of group separators we'll print. */ 1017 separators = getnumsep(ipos); 1018 1019 int padlen = width /* Minimum field width. */ 1020 - ipos /* Number of integer digits. */ 1021 - epos /* Number of exponent characters. */ 1022 - precision /* Number of fractional digits. */ 1023 - separators /* Number of group separators. */ 1024 - (emitpoint ? 1 : 0) /* Will we print a decimal point? */ 1025 - ((sign != 0) ? 1 : 0); /* Will we print a sign character? */ 1026 1027 if (padlen < 0) 1028 padlen = 0; 1029 1030 /* 1031 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is 1032 * ignored." (7.19.6.1, 6) 1033 */ 1034 if (flags & PRINT_F.MINUS) /* Left justifty. */ 1035 padlen = -padlen; 1036 else if (flags & PRINT_F.ZERO && padlen > 0) { 1037 if (sign != 0) { /* Sign. */ 1038 OUTCHAR(sign); 1039 sign = 0; 1040 } 1041 while (padlen > 0) { /* Leading zeros. */ 1042 OUTCHAR('0'); 1043 padlen--; 1044 } 1045 } 1046 while (padlen > 0) { /* Leading spaces. */ 1047 OUTCHAR(' '); 1048 padlen--; 1049 } 1050 if (sign != 0) /* Sign. */ 1051 OUTCHAR(sign); 1052 while (ipos > 0) { /* Integer part. */ 1053 ipos--; 1054 OUTCHAR(iconvert[ipos]); 1055 if (separators > 0 && ipos > 0 && ipos % 3 == 0) 1056 OUTCHAR(','); 1057 } 1058 if (emitpoint) { /* Decimal point. */ 1059 OUTCHAR('.'); 1060 } 1061 while (leadfraczeros > 0) { /* Leading fractional part zeros. */ 1062 OUTCHAR('0'); 1063 leadfraczeros--; 1064 } 1065 while (fpos > omitcount) { /* The remaining fractional part. */ 1066 fpos--; 1067 OUTCHAR(fconvert[fpos]); 1068 } 1069 while (epos > 0) { /* Exponent. */ 1070 epos--; 1071 OUTCHAR(econvert[epos]); 1072 } 1073 while (padlen < 0) { /* Trailing spaces. */ 1074 OUTCHAR(' '); 1075 padlen++; 1076 } 1077 } 1078 1079 int getnumsep(inout int digits) { 1080 inout const shared int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3; 1081 return separators; 1082 } 1083 1084 int getexponent(real value) { 1085 real tmp = (value >= 0.0) ? value : -value; 1086 int exponent = 0; 1087 1088 /* 1089 * We check for 99 > exponent > -99 in order to work around possible 1090 * endless loops which could happen (at least) in the second loop (at 1091 * least) if we're called with an infinite value. However, we checked 1092 * for infinity before calling this function using our ISINF() macro, so 1093 * this might be somewhat paranoid. 1094 */ 1095 while (tmp < 1.0 && tmp > 0.0 && --exponent > -99) 1096 tmp *= 10; 1097 while (tmp >= 10.0 && ++exponent < 99) 1098 tmp /= 10; 1099 1100 return exponent; 1101 } 1102 1103 int convert(uintmax_t value, char[] buf, int base, int caps) { 1104 const(char)* digits = caps ? "0123456789ABCDEF" : "0123456789abcdef"; 1105 size_t pos = 0; 1106 1107 /* We return an unterminated buffer with the digits in reverse order. */ 1108 do { 1109 buf[pos++] = digits[cast(size_t)(value % base)]; 1110 value /= base; 1111 } while (value != 0 && pos < buf.length); 1112 1113 return cast(int)pos; 1114 } 1115 1116 uintmax_t _cast(real value) { 1117 uintmax_t result; 1118 1119 /* 1120 * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be 1121 * represented exactly as an LDOUBLE value (but is less than LDBL_MAX), 1122 * it may be increased to the nearest higher representable value for the 1123 * comparison (cf. C99: 6.3.1.4, 2). It might then equal the LDOUBLE 1124 * value although converting the latter to UINTMAX_MAX would overflow. 1125 */ 1126 if (value >= UINTMAX_MAX) 1127 return UINTMAX_MAX; 1128 1129 result = cast(uintmax_t)value; 1130 /* 1131 * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to 1132 * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates 1133 * the standard). Sigh. 1134 */ 1135 return (result <= value) ? result : result - 1; 1136 } 1137 1138 uintmax_t myround(real value) { 1139 uintmax_t intpart = _cast(value); 1140 1141 return ((value -= intpart) < 0.5) ? intpart : intpart + 1; 1142 } 1143 1144 real mypow10(int exponent) { 1145 real result = 1; 1146 1147 while (exponent > 0) { 1148 result *= 10; 1149 exponent--; 1150 } 1151 while (exponent < 0) { 1152 result /= 10; 1153 exponent++; 1154 } 1155 return result; 1156 } 1157 1158 public: 1159 1160 int rpl_snprintf(A...)(char[] str, string format, A a) { 1161 mixin va_start!a; 1162 1163 int len = rpl_vsnprintf!dummy_file_func(str, format, va_args); 1164 return len; 1165 } 1166 1167 int rpl_fprintf(alias file_func, A...)(void* file, string format, A a) { 1168 mixin va_start!a; 1169 1170 int len = rpl_vfprintf!file_func(file, format, va_args); 1171 return len; 1172 } 1173 1174 int rpl_vfprintf(alias file_func)(void* file, string format, va_list ap) { 1175 if (file == null) { 1176 int len = rpl_vsnprintf!dummy_file_func(null, format, ap); 1177 return len; 1178 } else { 1179 char[1024] str = 0; 1180 int len = rpl_vsnprintf!file_func(str, format, ap, file); 1181 return len; 1182 } 1183 } 1184 1185 int rpl_asprintf(alias alloc_func, A...)(char[]* ret, string format, A a) { 1186 mixin va_start!a; 1187 1188 int len = rpl_vasprintf!alloc_func(ret, format, va_args); 1189 return len; 1190 } 1191 1192 int rpl_vasprintf(alias alloc_func)(char[]* ret, string format, va_list ap) { 1193 va_list aq; 1194 size_t size; 1195 va_copy(aq, ap); 1196 1197 int len = rpl_vsnprintf!dummy_file_func(null, format, aq); 1198 if (len < 0 || (*ret = (cast(char*)alloc_func(size = len + 1))[0..size]) is null) 1199 return -1; 1200 return rpl_vsnprintf!dummy_file_func(*ret, format, ap); 1201 }